package com.xhj.mybatis.mapping;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Map;
import java.util.Properties;

/**
 * VendorDatabaseIdProvider
 *
 * @author XJks
 * @description 基于数据库供应商名称的 DatabaseIdProvider 实现类，用于根据数据库的产品名称来确定数据库标识符。todo:由于目前没有多类数据库环境，暂时未使用该类编写测试代码。
 */
public class VendorDatabaseIdProvider implements DatabaseIdProvider {

    private static final Logger log = LoggerFactory.getLogger(VendorDatabaseIdProvider.class);

    /**
     * 用于存储用户自定义的数据库厂商名称与标识的映射关系
     */
    private Properties properties;

    @Override
    public String getDatabaseId(DataSource dataSource) {
        // 如果传入的数据源为 null，则抛出空指针异常
        if (dataSource == null) {
            throw new NullPointerException("dataSource cannot be null");
        }
        try {
            // 调用 getDatabaseName 方法获取数据库厂商标识
            return getDatabaseName(dataSource);
        } catch (Exception e) {
            // 如果获取过程中出现异常，记录错误日志
            log.error("Could not get a databaseId from dataSource", e);
        }
        // 如果发生异常，则返回 null
        return null;
    }

    @Override
    public void setProperties(Properties p) {
        this.properties = p;
    }

    /**
     * 根据数据源获取数据库厂商标识
     *
     * @param dataSource 数据源
     * @return 数据库厂商标识
     * @throws SQLException 数据库访问异常
     */
    private String getDatabaseName(DataSource dataSource) throws SQLException {
        // 获取数据库产品名称
        String productName = getDatabaseProductName(dataSource);
        // 如果用户自定义了映射关系
        if (this.properties != null) {
            // 遍历所有映射项
            for (Map.Entry<Object, Object> property : properties.entrySet()) {
                // 如果数据库产品名称包含某个 key，则返回对应的 value 作为标识
                if (productName.contains((String) property.getKey())) {
                    return (String) property.getValue();
                }
            }
            // 如果没有匹配项，则返回 null
            return null;
        }
        // 如果没有自定义映射，则直接返回数据库产品名称
        return productName;
    }

    /**
     * 获取数据库产品名称
     *
     * @param dataSource 数据源
     * @return 数据库产品名称
     * @throws SQLException 数据库访问异常
     */
    private String getDatabaseProductName(DataSource dataSource) throws SQLException {
        // 通过数据源获取数据库连接，并自动关闭连接
        try (Connection con = dataSource.getConnection()) {
            // 获取数据库元数据
            DatabaseMetaData metaData = con.getMetaData();
            // 返回数据库产品名称
            return metaData.getDatabaseProductName();
        }
    }
}
